package de.lmu.ifi.pst.uwejsf.framework.process;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;

public abstract class CallOperationAction extends Action {

	protected Map<String, Object> parameters = new HashMap<String, Object>();

	public abstract Class<?>[] getParameterTypes();

	public abstract String[] getParameterNames();

	public abstract void storeResultValue(Object result);

	public abstract Class<?> getTargetClass();

	public abstract String getMethodName();

	public abstract boolean isMethodConstructor();

	public void setInputValue(String name, Object value) {
		parameters.put(name, value);
	}

	@Override
	public void populateInputValues(Object bean) {
		try {

			Map<?, ?> props = BeanUtils.describe(bean);
			for (Object key : props.keySet()) {
				parameters.put((String) key, props.get(key));
			}

		} catch (Exception e) {
			throw new ProcessDataflowException(
					"Couldn't extract bean properties from bean of type "
							+ bean.getClass().toString(), e);
		}

	}

	public void execute() {
		Object result = null;

		if (isMethodConstructor()) {
			try {
				result = getConstructor().newInstance(getParameters());
			} catch (Exception e) {
				throw new ProcessDataflowException("Error calling constructor "
						+ " on class " + getTargetClass().getName(), e);
			}
		}

		else {
			Method m = getMethod();
			Object target = parameters.get("target");
			if ((m.getModifiers() & Modifier.STATIC) == 0 && target == null) {
				// not static
				throw new ProcessDataflowException(
						"No target given for CallOperationAction " + getName());
			}
			try {
				result = m.invoke(target, getParameters());
			} catch (Exception e) {
				throw new ProcessDataflowException("Error calling method "
						+ getMethodName() + " on class "
						+ getTargetClass().getName(), e);
			}
		}

		storeResultValue(result);
	}

	protected Method getMethod() {
		Method m = null;
		try {
			m = getTargetClass()
					.getMethod(getMethodName(), getParameterTypes());
		} catch (Exception e) {
			throw new ProcessDataflowException("Could not access method "
					+ getMethodName() + " on class "
					+ getTargetClass().getName(), e);
		}
		return m;
	}

	protected Constructor<?> getConstructor() {
		Constructor<?> c = null;
		try {
			c = getTargetClass().getConstructor(getParameterTypes());
		} catch (Exception e) {
			throw new ProcessDataflowException("Could not access method "
					+ getMethodName() + " on class "
					+ getTargetClass().getName(), e);
		}
		return c;
	}

	protected Object[] getParameters() {
		List<Object> paramList = new ArrayList<Object>();
		for (String paramName : getParameterNames()) {
			paramList.add(parameters.get(paramName));
		}
		return paramList.toArray();

	}

	public CallOperationAction(ProcessActivityBean activity) {
		super(activity);

	}

}
